
// Copyright (C) 2009-2011 Luca Piccioni
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.IO;

namespace Derm.BindGen
{
	class FunctionClassFactory : PartialClassFactory
	{
		#region Constructors

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ns"></param>
		/// <param name="cName"></param>
		/// <param name="cFuncts"></param>
		public FunctionClassFactory(string ns, string cName, IDictionary<string,Function> cFuncts) : base(ns, cName)
		{
			mFunctions = new SortedList<string, Function>(cFuncts);
		}

		/// <summary>
		/// Enums to define.
		/// </summary>
		private readonly IDictionary<string,Function> mFunctions;

		#endregion

		#region Source Code Generation

		public override void WriteAssembly(string path, SourceExt exts)
		{
			/* Extended function interfaces */
			if ((exts & SourceExt.ARB) != 0)
				WriteAssemblyImplementation(path+ClassName+"FunctsARB.cs", WriteArbClassImplementation);
			if ((exts & SourceExt.EXT) != 0)
				WriteAssemblyImplementation(path+ClassName+"FunctsEXT.cs", WriteExtClassImplementation);
			if ((exts & SourceExt.ATI) != 0)
				WriteAssemblyImplementation(path+ClassName+"FunctsATI.cs", WriteAtiClassImplementation);
			if ((exts & SourceExt.NV) != 0)
				WriteAssemblyImplementation(path+ClassName+"FunctsNV.cs", WriteNvClassImplementation);
			if ((exts & SourceExt.OTHERS) != 0)
				WriteAssemblyImplementation(path+ClassName+"FunctsOthers.cs", WriteOthersClassImplementation);
			// Core implementation
			WriteAssemblyImplementation(path+ClassName+"Functs.cs", WriteBaseClassImplementation);
		}

		protected void WriteBaseClassImplementation(StreamWriter sw)
		{
			WriteExtensionClassImplementation(sw, null, sExcludeExtFilter);
		}

		protected void WriteArbClassImplementation(StreamWriter sw)
		{
			WriteExtensionClassImplementation(sw, new string[] { "ARB" }, sExcludeExtFilter);
		}

		protected void WriteExtClassImplementation(StreamWriter sw)
		{
			WriteExtensionClassImplementation(sw, new string[] { "EXT" }, sExcludeExtFilter);
		}

		protected void WriteAtiClassImplementation(StreamWriter sw)
		{
			WriteExtensionClassImplementation(sw, new string[] { "ATI", "AMD" }, sExcludeExtFilter);
		}

		protected void WriteNvClassImplementation(StreamWriter sw)
		{
			WriteExtensionClassImplementation(sw, new string[] { "NV" }, sExcludeExtFilter);
		}

		protected void WriteOthersClassImplementation(StreamWriter sw)
		{
			WriteExtensionClassImplementation(sw, sExcludeExtFilter, null);
		}

		protected void WriteExtensionClassImplementation(StreamWriter sw, string[] incPrefix, string[] exPrefix)
		{
			Function[] mFilteredFunct = new Function[mFunctions.Values.Count];
			List<string> mRemovedKeys = new List<string>();

			mFunctions.Values.CopyTo(mFilteredFunct, 0);
			mFilteredFunct = Array.FindAll<Function>(mFilteredFunct, delegate (Function sFunct) {
				if (exPrefix != null) {
					bool skip = false;

					foreach (string p in exPrefix)
						if ((sFunct.ImportName.EndsWith(p) == true) || ((sFunct.Category != null) && (sFunct.Category.EndsWith(p) == true))) {
							skip = true;
							break;
						}

					if (skip == true)
						return (false);
				}
				if (incPrefix != null) {
					foreach (string p in incPrefix) {
						if (sFunct.ImportName.EndsWith(p) == true) {
							return (true);
						}
					}
					return (false);
				} else {
					return (true);
				}
			});

			#region Function Implementations

			// Write implementations
			foreach (Function sFunct in mFilteredFunct) {
				if ((sFunct.HasSuperset == false) && (sFunct.ImplementationExcluded == false)) {
					WriteFunctionImplementation(sw, sFunct);
				}
			}

			#endregion

			#region Function Delegates

			// Write delegates
			sw.WriteLine("		internal static partial class Delegates");
			sw.WriteLine("		{");
			foreach (Function sFunct in mFilteredFunct)
				WriteFunctionDelegate(sw, sFunct);
			sw.WriteLine("		}");
			sw.WriteLine();

			#endregion

			#region Function Imports

			// Write imports
			sw.WriteLine("		internal static partial class Imports");
			sw.WriteLine("		{");
			foreach (Function sFunct in mFilteredFunct) {
				WriteFunctionInport(sw, sFunct);
				mRemovedKeys.Add(sFunct.ImportName);
			}
			sw.WriteLine("		}");
			sw.WriteLine();

			#endregion

			// Remove inserted items
			foreach (Function k in mFilteredFunct)
				mFunctions.Remove(k.ImportName);
		}

		private void WriteFunctionInport(StreamWriter sw, Function sFunct)
		{
			string[] import;

			// Generate input code
			import = sFunct.GetImportDeclaration();
			foreach (string s in import)
				sw.WriteLine("			"+s);
			sw.WriteLine();
		}

		private void WriteFunctionDelegate(StreamWriter sw, Function sFunct)
		{
			string[] import;

			// Generate input code
			import = sFunct.GetDelegateDeclaration();
			foreach (string s in import)
				sw.WriteLine("			"+s);
			sw.WriteLine();
		}

		private void WriteFunctionImplementation(StreamWriter sw, Function sFunct)
		{
			string[] implementation;

			// Generate default implementation
			implementation = sFunct.GetImplementation();
			foreach (string s in implementation)
				sw.WriteLine("		"+s);
			sw.WriteLine();

			// Generate overloaded implementation
			if ((implementation = sFunct.GetOverloadedImplementations()) != null) {
				foreach (string s in implementation)
					sw.WriteLine("		"+s);
				sw.WriteLine();
			}
		}

		private static readonly string[] sExcludeExtFilter = new string[] {
			"3DFX",
			"APPLE",
			"GREMEDY",
			"HP",
			"IMG",
			"I3D",
			"IBM",
			"IMG",
			"INGR",
			"INTEL",
			"MESA", "MESAX",
			"OES",
			"OML",
			"PGI",
			"QCOM",
			"REND",
			"S3",
			"SGI", "SGIS", "SGIX",
			"SUN", "SUNX",
			"WIN"
		};

		#endregion
	}
}